#include "degfx/driver.h"

#ifdef MX__DRIVER_WIN32_GDI

#   include <windows.h>

#   ifndef MX_DEGFX_ARGB8888
#      error DEGFX: Win32 GDI driver requires ARGB8888 color encoding
#   endif

extern int main(int argc, char *argv[]);

static int mx__win32_gdi_keyscan;
static int mx__win32_gdi_keyascii;
static unsigned mx__win32_gdi_keypress = false;
static unsigned mx__win32_gdi_mouseinfo = false;
static unsigned mx__win32_gdi_init = false;

static char mx__win32_gdi_classname[] = "DegfxApp";
static HWND mx__win32_gdi_hwnd;
static HDC mx__win32_gdi_hdc;

static void mx__wingdi_flush(const MX_RECT * rect, MX_PIXEL * array, const int stride)
{
	BITMAPINFO bi;
	const int h = rect->y2 - rect->y1 + 1;

	ZeroMemory(&bi.bmiHeader, sizeof(BITMAPINFOHEADER));
	bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	bi.bmiHeader.biBitCount = 32;
	bi.bmiHeader.biPlanes = 1;
	bi.bmiHeader.biWidth = stride;
	bi.bmiHeader.biHeight = -h;
	bi.bmiHeader.biClrUsed = 256;
	bi.bmiHeader.biCompression = BI_RGB;

	/* Push the buffer to the visible screen, letting GDI handle the clipping 
	 */
	StretchDIBits(mx__win32_gdi_hdc, rect->x1, rect->y1, stride, h, 0, 0, stride, h, array, &bi, DIB_RGB_COLORS, SRCCOPY);
}

static LRESULT CALLBACK mx__wingdi_winproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message) {

	case WM_PAINT:
		{
			MX_RECT rect;
			PAINTSTRUCT paintstruct;

			if (!mx__win32_gdi_init)
				break;

			mx__win32_gdi_hdc = BeginPaint(hwnd, &paintstruct);

			/* Force a redrawing of the screen now */
			rect.x1 = paintstruct.rcPaint.left;
			rect.y1 = paintstruct.rcPaint.top;
			rect.x2 = paintstruct.rcPaint.right - 1;
			rect.y2 = paintstruct.rcPaint.bottom - 1;
			mx__gfx_update(mx__wingdi_flush, &rect);

			EndPaint(hwnd, &paintstruct);
			return 0;
		}

	case WM_SIZE:
		mx__args.screen.x1 = 0;
		mx__args.screen.y1 = 0;
		mx__args.screen.x2 = LOWORD(lParam);
		mx__args.screen.y2 = HIWORD(lParam);
		mx__args.w = LOWORD(lParam);
		mx__args.h = HIWORD(lParam);
		break;

	case WM_MOUSEMOVE:
		mx__mousex = LOWORD(lParam);
		mx__mousey = HIWORD(lParam);
		mx__win32_gdi_mouseinfo = true;
		break;

	case WM_LBUTTONDOWN:
		mx__mouseb = true;
		mx__win32_gdi_mouseinfo = true;
		break;

	case WM_LBUTTONUP:
		mx__mouseb = false;
		mx__win32_gdi_mouseinfo = true;
		break;

	case WM_CHAR:
		mx__win32_gdi_keyascii = wParam & 0xff;
		mx__win32_gdi_keyscan = ((lParam >> 16) & 0xff);
		mx__win32_gdi_keypress = true;
		break;

	case WM_KEYDOWN:
		mx__win32_gdi_keyascii = 0;
		mx__win32_gdi_keyscan = ((lParam >> 16) & 0xff);
		mx__win32_gdi_keypress = true;
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;
	}

	return DefWindowProc(hwnd, message, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil)
{
	WNDCLASSEX wincl;

	(void) hPrevInstance;
	(void) lpszArgument;

	/* The Window structure */
	wincl.hInstance = hThisInstance;
	wincl.lpszClassName = mx__win32_gdi_classname;
	wincl.lpfnWndProc = mx__wingdi_winproc;
	wincl.style = 0;
	wincl.cbSize = sizeof(WNDCLASSEX);

	/* Use default icon and mouse-pointer */
	wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
	wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
	wincl.lpszMenuName = NULL;
	wincl.cbClsExtra = 0;
	wincl.cbWndExtra = 0;
	wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

	/* Register the window class, and if it fails quit the program */
	if (!RegisterClassEx(&wincl))
		return 0;

	/* The class is registered, let's create the program */
	mx__win32_gdi_hwnd = CreateWindowEx(0,	/* Extended possibilites for
											   variation */
										mx__win32_gdi_classname, "Windows App",	/* Title 
																				   Text 
																				 */
										WS_OVERLAPPEDWINDOW,	/* default
																   window */
										CW_USEDEFAULT,	/* Windows decides
														   the position */
										CW_USEDEFAULT,	/* where the window
														   ends up on the
														   screen */
										544,	/* The programs width */
										375,	/* and height in pixels */
										HWND_DESKTOP,	/* The window is a
														   child-window to
														   desktop */
										NULL,	/* No menu */
										hThisInstance,	/* Program Instance
														   handler */
										NULL	/* No Window Creation data */
		);

	/* Make the window visible on the screen */
	ShowWindow(mx__win32_gdi_hwnd, nFunsterStil);

	/* Call the C-style main function which will call poll to pump messages */
	mx__win32_gdi_init = false;
	return main(0, 0);
}

static unsigned mx__win32_gdi_start(MX_GFX_ARGS * args)
{
	if (args->w == 0)
		args->w = 544;
	if (args->h == 0)
		args->h = 375;

	SetWindowPos(mx__win32_gdi_hwnd, 0, 0, 0, args->w, args->h, SWP_NOZORDER | SWP_NOMOVE);

	if (args->title)
		SetWindowText(mx__win32_gdi_hwnd, args->title);

	args->title = "win32gdi";
	args->pointer = true;

	mx__win32_gdi_init = true;

	return true;
}

static void mx__win32_gdi_stop(void)
{
	mx__win32_gdi_init = false;
}

static unsigned mx__win32_gdi_poll(void)
{
	MSG messages;

	if (GetMessage(&messages, NULL, 0, 0)) {
		TranslateMessage(&messages);
		DispatchMessage(&messages);
		return true;
	}
	return false;
}

static void mx__win32_gdi_dirty(const MX_RECT * rect)
{
	RECT area;

	area.left = rect->x1;
	area.top = rect->y1;
	area.right = rect->x2 + 1;
	area.bottom = rect->y2 + 1;

	InvalidateRect(mx__win32_gdi_hwnd, &area, FALSE);
}

static unsigned mx__win32_gdi_pointer(void)
{
	if (!mx__win32_gdi_mouseinfo)
		return false;

	mx__win32_gdi_mouseinfo = false;
	return true;
}

static unsigned mx__win32_gdi_key(int *scan, int *ascii)
{
	if (!mx__win32_gdi_keypress)
		return false;

	*scan = mx__win32_gdi_keyscan;
	*ascii = mx__win32_gdi_keyascii;
	mx__win32_gdi_keypress = false;
	return true;
}

const MX_DRIVER mx__win32_gdi = {
	mx__win32_gdi_start,
	mx__win32_gdi_stop,

	mx__win32_gdi_poll,
	mx__win32_gdi_dirty,

	mx__win32_gdi_pointer,
	mx__win32_gdi_key
};

#endif
